;==============================================================================
; SimpleOS 32位引导扇区
; 功能: 从磁盘加载内核 → 开启A20 → 切换保护模式 → 跳转到内核
;==============================================================================
bits 16
org 0x7c00

KERNEL_OFFSET equ 0x1000    ; 内核加载到的内存地址
KERNEL_SECTORS equ 22       ; 内核占用的扇区数（根据 kernel.bin 大小调整）

start:
    ; 初始化段寄存器和栈
    cli                     ; 关闭中断
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00          ; 栈向下增长
    
    ; 保存BIOS传入的启动设备号（非常重要！）
    mov [boot_drive], dl
    
    sti                     ; 开启中断
    
    ; 立即显示一个字符，测试显示功能
    mov ah, 0x0e
    mov al, 'B'
    int 0x10
    
    ; 显示启动消息
    mov si, msg_boot
    call print_string
    
    ; 从磁盘加载内核（扇区2开始，共10个扇区）
    call load_kernel
    
    ; 显示A20消息
    mov si, msg_a20
    call print_string
    
    ; 开启A20地址线
    call enable_a20
    
    ; 显示GDT消息
    mov si, msg_gdt
    call print_string
    
    ; 加载GDT
    lgdt [gdt_descriptor]
    
    ; 显示保护模式切换消息
    mov si, msg_pm_switch
    call print_string
    
    ; 切换到保护模式
    mov eax, cr0
    or eax, 1
    mov cr0, eax
    
    ; 远跳转清除CPU流水线，进入32位代码段
    jmp CODE_SEG:init_pm

;==============================================================================
; 16位函数：从磁盘加载内核
;==============================================================================
load_kernel:
    mov si, msg_load
    call print_string
    
    ; 首先重置磁盘控制器
    mov ah, 0x00            ; BIOS磁盘重置功能
    mov dl, [boot_drive]    ; 使用保存的启动设备号
    int 0x13                ; 调用BIOS
    jc disk_error           ; 重置失败
    
    ; 读取内核扇区
    mov ah, 0x02            ; BIOS读扇区功能
    mov al, KERNEL_SECTORS  ; 读取内核所需的扇区数
    mov ch, 0               ; 柱面0
    mov cl, 2               ; 从扇区2开始（扇区从1开始计数）
    mov dh, 0               ; 磁头0
    mov dl, [boot_drive]    ; 使用保存的启动设备号
    mov bx, KERNEL_OFFSET   ; 目标地址 ES:BX = 0:0x1000
    int 0x13                ; 调用BIOS
    jc disk_error           ; 出错则跳转
    
    ; 验证读取的扇区数
    cmp al, KERNEL_SECTORS  ; AL返回实际读取的扇区数
    jne disk_error          ; 如果不等于10，说明有问题
    
    mov si, msg_ok
    call print_string
    ret

disk_error:
    mov si, msg_err
    call print_string
    mov si, msg_err_detail
    call print_string
    
    ; 显示错误码（AH中包含错误代码）
    mov al, ah
    call print_hex
    
    jmp $

;==============================================================================
; 16位函数：开启A20地址线（方法1: 快速方式）
;==============================================================================
enable_a20:
    in al, 0x92
    or al, 2
    out 0x92, al
    ret

;==============================================================================
; 16位函数：打印字符串 (SI指向字符串)
;==============================================================================
print_string:
    pusha
    mov ah, 0x0e            ; BIOS字符显示功能
.loop:
    lodsb
    test al, al
    jz .done
    int 0x10
    jmp .loop
.done:
    popa
    ret

;==============================================================================
; 16位函数：打印十六进制字节 (AL=要打印的字节)
;==============================================================================
print_hex:
    pusha
    mov cx, 2               ; 打印2个十六进制字符
.loop:
    rol al, 4               ; 循环左移4位
    push ax
    and al, 0x0f            ; 取低4位
    cmp al, 9
    jle .digit
    add al, 7               ; A-F
.digit:
    add al, '0'             ; 转换为ASCII
    mov ah, 0x0e
    int 0x10
    pop ax
    loop .loop
    popa
    ret

;==============================================================================
; GDT - 全局描述符表
;==============================================================================
gdt_start:
    ; 空描述符
    dq 0

gdt_code:                   ; 代码段描述符
    dw 0xffff               ; 段限长 0-15
    dw 0x0000               ; 基址 0-15
    db 0x00                 ; 基址 16-23
    db 10011010b            ; 访问字节: Pr=1, Privl=00, S=1, Ex=1, DC=0, RW=1, Ac=0
    db 11001111b            ; 标志: Gr=1, Sz=1, 限长16-19=0xf
    db 0x00                 ; 基址 24-31

gdt_data:                   ; 数据段描述符
    dw 0xffff
    dw 0x0000
    db 0x00
    db 10010010b            ; Pr=1, Privl=00, S=1, Ex=0, DC=0, RW=1, Ac=0
    db 11001111b
    db 0x00

gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

;==============================================================================
; 32位保护模式初始化
;==============================================================================
bits 32
init_pm:
    ; 设置所有段寄存器
    mov ax, DATA_SEG
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    
    ; 设置32位栈
    mov ebp, 0x90000
    mov esp, ebp
    
    ; 测试：在保护模式下显示一个字符（在屏幕左上角）
    ; 这确保保护模式代码能够执行
    mov edi, 0xb8000
    mov ax, 0x0f50          ; 'P' 白色
    mov [edi], ax
    
    ; 直接跳转到内核（内核会自己清屏和显示信息）
    mov eax, KERNEL_OFFSET
    jmp eax
    
    ; 如果内核返回，则挂起
    jmp $

;==============================================================================
; 32位函数：清屏（保留，但不在init_pm中使用）
;==============================================================================
clear_screen_pm:
    pusha
    mov edi, 0xb8000
    mov ecx, 80 * 25
    mov ax, 0x0f20          ; 白色空格
    rep stosw
    popa
    ret

;==============================================================================
; 32位函数：打印字符串 (EBX指向字符串) - 修复版
;==============================================================================
print_string_pm:
    pusha
    mov edi, 0xb8000        ; 显存起始地址
    mov ah, 0x0f            ; 白色前景，黑色背景
.loop:
    mov al, [ebx]           ; 读取字符
    cmp al, 0               ; 检查字符串结束
    je .done
    mov [edi], ax           ; 写入显存
    add ebx, 1              ; 下一个字符
    add edi, 2              ; 下一个显存位置（每个字符2字节）
    jmp .loop
.done:
    popa
    ret

;==============================================================================
; 数据区
;==============================================================================
msg_boot:       db '[BOOT] SimpleOS Starting...', 13, 10, 0
msg_load:       db '[BOOT] Loading kernel...', 13, 10, 0
msg_ok:         db '[BOOT] OK', 13, 10, 0
msg_err:        db '[BOOT] Disk read error!', 13, 10, 0
msg_err_detail: db '[BOOT] Error code: 0x', 0
msg_a20:        db 'A20', 13, 10, 0
msg_gdt:        db 'GDT', 13, 10, 0
msg_pm_switch:  db 'PM', 13, 10, 0
msg_pm:         db 'Protected Mode', 0

boot_drive:     db 0        ; 保存BIOS传入的启动设备号

;==============================================================================
; 填充引导扇区
;==============================================================================
times 510 - ($ - $$) db 0
dw 0xaa55

